﻿#region Using References

using System.Collections.Generic;
using Beaker.OpenCube.CubeManager.Model.Framework;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

#endregion

namespace Beaker.OpenCube.CubeManager.Specs
{
	[TestClass]
	public class EntityCollectionSpecs
	{
		#region 1-n

		[TestMethod]
		public void WhenChildAddedThenParentSetOnChild()
		{
			// arrange
			var parent = new EntityParent();
			var child = new EntityChild();

			// act
			parent.Children.Add( child );

			// assert
			child.Parent
				.Should().BeSameAs( parent );
		}

		[TestMethod]
		public void WhenChildAddedThenChildRemovedFromOldParent()
		{
			// arrange
			var oldParent = new EntityParent();
			var child = new EntityChild();
			oldParent.Children.Add( child );
			var newParent = new EntityParent();

			// act
			newParent.Children.Add( child );

			// assert
			oldParent.Children
				.Should().NotContain( child );
		}

		[TestMethod]
		public void WhenParentSetThenChildAddedToSet()
		{
			// arrange
			var parent = new EntityParent();
			var child = new EntityChild();

			// act
			child.Parent = parent;

			// assert
			parent.Children
				.Should().Contain( child );
		}

		[TestMethod]
		public void WhenParentSetThenChildRemovedFromOldSet()
		{
			// arrange
			var parent = new EntityParent();
			var child = new EntityChild();
			var oldParent = new EntityParent();
			oldParent.Children.Add( child );

			// act
			child.Parent = parent;

			// assert
			oldParent.Children
				.Should().NotContain( child );
		}

		[TestMethod]
		public void WhenParentClearedThenChildRemovedFromSet()
		{
			// arrange
			var parent = new EntityParent();
			var child = new EntityChild();
			parent.Children.Add( child );

			// act
			child.Parent = null;

			// assert
			parent.Children
				.Should().NotContain( child );
		}

		#region Specs HelperClasses

		private class EntityParent
			: Entity
		{
			private readonly EntitySet<EntityChild, EntityParent> _children;

			public EntityParent()
			{
				_children = new EntitySet<EntityChild, EntityParent>( this, ( child, parent ) => child.Parent = parent );
			}

			public ISet<EntityChild> Children
			{
				get { return _children; }
			}
		}

		private class EntityChild
			: Entity
		{
			private EntityParent _parent;

			public EntityParent Parent
			{
				get { return _parent; }
				set { SetAndSync( ref _parent, value, parent => parent.Children ); }
			}
		}

		#endregion
		
		#endregion
		#region n-n

		[TestMethod]
		public void WhenAddedToCollectionThenAlsoAddToReverseCollection()
		{
			// arrange
			var left = new EntityLeft();
			var right = new EntityRight();

			// act
			left.Rights.Add( right );

			// assert
			right.Lefts
				.Should().Contain( left );
		}

		[TestMethod]
		public void WhenRemovedFromCollectionThenAlsoRemovedFromReverseCollection()
		{
			// arrange
			var left = new EntityLeft();
			var right = new EntityRight();
			right.Lefts.Add( left );

			// act
			left.Rights.Remove( right );

			// assert
			right.Lefts
				.Should().NotContain( left );
		}

		#region Specs HelperClasses

		private class EntityLeft
			: Entity
		{
			private readonly EntitySet<EntityRight, EntityLeft> _rights;

			public EntityLeft()
			{
				_rights = new EntitySet<EntityRight, EntityLeft>( this, right => right.Lefts );
			}

			public ISet<EntityRight> Rights
			{
				get { return _rights; }
			}
		}

		private class EntityRight
			: Entity
		{
			private readonly EntitySet<EntityLeft, EntityRight> _lefts;

			public EntityRight()
			{
				_lefts = new EntitySet<EntityLeft, EntityRight>( this, left => left.Rights );
			}

			public ISet<EntityLeft> Lefts
			{
				get { return _lefts; }
			}
		}

		#endregion

		#endregion
	}
}
